#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "link.h"
#include "logger.h"

#define MAX_LOG_MESSAGE_LEN 1024

typedef struct log_event_t
{
	int id;
	char message[MAX_LOG_MESSAGE_LEN];
} log_event;

static void* write_proc(void *arg);
static void write_log_event(logger_t* l, log_event* le);

int logger_init(logger_t* l, const char* file)
{
	if (l == NULL || file == NULL) {
		return LOGGER_ERROR;
	}

	l->file = fopen(file, "wb+");
	if (l->file == NULL) {
		return LOGGER_ERROR;
	}

	if (mq_init(&(l->mq)) != LIST_SUCCESS) {
		fclose(l->file);
		return LOGGER_ERROR;
	}

	l->run_flag = 1;
	l->seq = 1;

	if (pthread_create(&(l->write_thread), NULL, write_proc, l) != 0) {
		mq_destroy(&(l->mq));
		fclose(l->file);
		return LOGGER_ERROR;
	}

	return LOGGER_SUCCESS;
}

int logger_add_message(logger_t* l, int id, char* message)
{
	log_event* le;

	if (l == NULL) {
		return LOGGER_ERROR;
	}

	if ((le = (log_event *) malloc(sizeof(log_event))) == NULL) {
		return LOGGER_ERROR;
	}

	le->id = id;
	strncpy(le->message, message, MAX_LOG_MESSAGE_LEN);

	if (mq_enqueue(&(l->mq), le) != MQ_SUCCESS) {
		free(le);
		return LOGGER_ERROR;
	}

	return LOGGER_SUCCESS;
}

int logger_destroy(logger_t* l)
{
	int mrc, frc;
	log_event* le;

	if (l == NULL) {
		return LOGGER_ERROR;
	}

	l->run_flag = 0;
	mq_enqueue(&(l->mq), NULL); /* send dummy to exit blocking mq_dequeue */
	pthread_join(l->write_thread, NULL);

	while (mq_dequeue(&(l->mq), (mq_message *) &le, 0) != MQ_EMPTY) {
		if (le != NULL) {
			write_log_event(l, le);
			free(le);
		}
	}

	mrc = mq_destroy(&(l->mq));
	assert(mrc == MQ_SUCCESS);

	frc = fflush(l->file);
	assert(frc == 0);

	frc = fclose(l->file);
	assert(frc == 0);

	return LOGGER_SUCCESS;
}

static void* write_proc(void *arg)
{
	logger_t* l = (logger_t *) arg;

	while(l->run_flag) {
		log_event* le = NULL;
		int mrc;

		mrc = mq_dequeue(&(l->mq), (mq_message *) &le, 1);
		assert(mrc == MQ_SUCCESS || !(l->run_flag));

		if (le != NULL) {
			write_log_event(l, le);
			free(le);
		}
	}

	return 0;
}

void write_log_event(logger_t* l, log_event* le)
{
	fprintf(l->file, "%d, %d, %s", l->seq++, le->id, le->message);
	fflush(l->file);
}
